package org.hs.phd.odi.tracking.tracker.histogram;

import java.awt.Polygon;
import java.util.Arrays;

import org.hs.phd.common.improc.traversal.RGBImagePixelProcessor;
import org.hs.phd.odi.tracking.tracker.Histogram;
import org.hs.phd.odi.tracking.tracker.TrackWindow;
import org.hs.phd.odi.tracking.tracker.kernel.EpanechnikovKernel;
import org.hs.phd.odi.tracking.tracker.kernel.Kernel;

import com.googlecode.javacv.cpp.opencv_core.IplImage;

public class Weighted3DHistogram implements Histogram {
	
	
	class HistogramCalculatingPixelProcessor implements RGBImagePixelProcessor{
	
		
		private float histogramSum;
		private final TrackWindow trackWindow;
		
		public HistogramCalculatingPixelProcessor(TrackWindow trackWindow){
			this.trackWindow = trackWindow;
			histogramSum = 0;
		}

		@Override
		public void processPixel(int x, int y, int r, int g, int b){
			int histogramIndex = getHistogramIndex(r, g, b);
			
			float distance = trackWindow.normalizedDistanceFromGridCenterTo(x, y);
			float value = kernel.value(distance);

			histogramSum += value;
			histogram[histogramIndex] += value;
		}

		public float getHistogramSum() {
			return histogramSum;
		}

	}

    private int binsR;
    private int binsG;
    private int binsB;

    private float[] histogram;
	private final Kernel kernel;

    public Weighted3DHistogram(int binsR, int binsG, int binsB, Kernel kernel) {
        this.binsR = binsR;
        this.binsG = binsG;
        this.binsB = binsB;
		this.kernel = kernel;

        int size = getHistogramIndex(255, 255, 255) + 1;
        histogram = new float[size];
    }
    
    public Weighted3DHistogram() {
    	this(32,32,16, new EpanechnikovKernel());
	}

	public final int getHistogramIndex(int nR, int nG, int nB) {
        int qB = nB * binsB/256;
        int qG = nG * binsG/256;
        int qR = nR * binsR/256;

        return qB + qG * binsB + qR * binsB * binsG;
    }

	@Override
	public Kernel getKernel() {
		return kernel;
	}

	@Override
	public void calculateFrom(IplImage frame, TrackWindow trackWindow) {
		reset();
		
		HistogramCalculatingPixelProcessor processor = new HistogramCalculatingPixelProcessor(trackWindow);
		trackWindow.traversePixels(frame, processor);

		normalize(processor.getHistogramSum());
	}

	private void normalize(float histogramSum) {
		if( histogramSum > 0 ){
			for(int i = 0; i < histogram.length; i++){
				histogram[i]= histogram[i]/histogramSum;
			}
		}
	}

	@Override
	public void reset() {
		for(int i = 0; i < histogram.length; i++){
			histogram[i] = 0;
		}
		
	}

	@Override
	public float[] getData() {
		return histogram;
	}

	@Override
	public Histogram getCopy() {
		Weighted3DHistogram copy = new Weighted3DHistogram(binsR, binsG, binsB, kernel);
		System.arraycopy(histogram, 0, copy.histogram, 0, histogram.length);
		return copy;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + binsB;
		result = prime * result + binsG;
		result = prime * result + binsR;
		result = prime * result + Arrays.hashCode(histogram);
		result = prime * result + ((kernel == null) ? 0 : kernel.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Weighted3DHistogram other = (Weighted3DHistogram) obj;
		if (binsB != other.binsB)
			return false;
		if (binsG != other.binsG)
			return false;
		if (binsR != other.binsR)
			return false;
		if (!Arrays.equals(histogram, other.histogram))
			return false;
		if (kernel == null) {
			if (other.kernel != null)
				return false;
		} else if (kernel.getClass() != other.kernel.getClass())
			return false;
		return true;
	}


}
